package com.client;

import com.Async.Callback;
import com.Async.RpcFuture;
import com.loading.HashStrategy;
import com.loading.Strategy;
import com.registration.Register;
import com.serializetool.Request;
import com.serializetool.SnowflakeIdGenerator;
import org.apache.curator.framework.recipes.cache.CuratorCache;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Future;

public class ClientTerminal {
    ClientBuilder builder ;
    Register register;
    SnowflakeIdGenerator snowflakeIdGenerator;
    Strategy strategy;
    private static volatile ClientTerminal terminal;

    private ClientTerminal(){
        this(128,8);
    }

    private ClientTerminal(int maxCache,int workerId){
        builder = new ClientBuilder();
        register = new Register();
        snowflakeIdGenerator = new SnowflakeIdGenerator(workerId);
        strategy = new HashStrategy();
    }

    public static ClientTerminal getClientTerminal(){
        if(terminal==null){
            synchronized(ClientTerminal.class){
                if(terminal==null) terminal = new ClientTerminal();
            }
        }
        return terminal;
    }

    public Future remoteCall(Request request, boolean async, Callback callback, String serviceName) throws Exception {
        InetSocketAddress address;
        //看看是否已有连接，有就直接使用
        RpcFuture future = new RpcFuture();
        try {
            future.setCallback(callback);
            long id = snowflakeIdGenerator.getId();
            request.setTaskId(id);
            future.setTaskId(id);
            CuratorCache cache = register.getWatcher(serviceName);
            //减去根节点
            int s = cache.size() - 1;
            int choose = strategy.tryChoose(s, id);
            String detailServiceName = getCompleteServicePath(serviceName, choose);
            String strSocket = new String(cache.get(detailServiceName).get().getData(), StandardCharsets.UTF_8);
            future.setServiceName(serviceName);
            address = getAddress(strSocket);
            future.setSocket(address);
            if (!async) {
                while (!future.isDone()) {
                    Thread.sleep(1000);
                }
            }
            builder.send(address,request,future);
        }catch(Exception e){
            future.setError(e);
        }
        return future;
    }

    private String getCompleteServicePath(String serviceName,int choose){
        String num = Integer.toBinaryString(choose);
        int rest = 10-num.length();
        StringBuilder builder = new StringBuilder(serviceName);
        builder.append("/provider_");
        while(rest-->0){
            builder.append("0");
        }
        builder.append(num);
        return builder.toString();
    }

    private InetSocketAddress getAddress(String servicePath){
        String[] str = servicePath.split(":");
        int len = str[1].length();
        return new InetSocketAddress(str[0],Integer.parseInt(str[1]));
    }

    public synchronized void close() throws Exception{
        register.close();
        builder.close();
    }
}
